package fr.jade.fraclite.deployment;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.Interface;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.Type;
import org.objectweb.fractal.api.control.BindingController;
import org.objectweb.fractal.api.control.IllegalBindingException;
import org.objectweb.fractal.api.control.IllegalLifeCycleException;
import org.objectweb.fractal.api.control.LifeCycleController;
import org.objectweb.fractal.api.factory.GenericFactory;
import org.objectweb.fractal.api.type.InterfaceType;
import org.objectweb.fractal.api.type.TypeFactory;
import org.objectweb.fractal.deployment.local.api.IModuleRepository;
import org.objectweb.fractal.deployment.local.api.IPhysicalPackage;
import org.objectweb.fractal.deployment.local.api.IPhysicalPackageRepository;
import org.objectweb.fractal.util.Fractal;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import fr.jade.fraclite.FracLite;

public class BasicModuleRepository implements IModuleRepository,
		BindingController, LifeCycleController {

	Map<String, Component> moduleBase = new HashMap<String, Component>();

	TypeFactory tf = null;

	GenericFactory gf = null;

	boolean started = false;

	Component thisComponent = null;

	Component bootstrapComposite = null;

	IPhysicalPackageRepository packageRepo = null;

	public String[] listFc() {
		return new String[] { "gf", "tf", "pr" };
	}

	public Object lookupFc(String arg0) throws NoSuchInterfaceException {
		if (arg0.equals("gf")) {
			return this.gf;
		} else if (arg0.equals("tf")) {
			return this.tf;
		} else if (arg0.equals("pr")) {
			return this.packageRepo;
		}
		return null;
	}

	public void bindFc(String arg0, Object arg1)
			throws NoSuchInterfaceException, IllegalBindingException,
			IllegalLifeCycleException {
		if (arg0.equals("gf")) {
			this.gf = (GenericFactory) arg1;
		} else if (arg0.equals("tf")) {
			this.tf = (TypeFactory) arg1;
		} else if (arg0.equals("component")) {
			this.thisComponent = (Component) arg1;
		} else if (arg0.equals("pr")) {
			this.packageRepo = (IPhysicalPackageRepository) arg1;
		}
	}

	public void unbindFc(String arg0) throws NoSuchInterfaceException,
			IllegalBindingException, IllegalLifeCycleException {
		if (arg0.equals("gf")) {
			this.gf = null;
		} else if (arg0.equals("tf")) {
			this.tf = null;
		} else if (arg0.equals("pr")) {
			this.packageRepo = null;
		}
	}

	public String getFcState() {
		if (started) {
			return LifeCycleController.STARTED;
		} else {
			return LifeCycleController.STOPPED;
		}
	}

	public void startFc() throws IllegalLifeCycleException {
		if (started) {
			return;
		}

		Component[] parents;
		try {
			parents = Fractal.getSuperController(thisComponent)
					.getFcSuperComponents();
			for (int i = 0; i < parents.length; i++) {
				String name = Fractal.getNameController(parents[i]).getFcName();
				if (name.equals("bootstrapComposite")) {
					bootstrapComposite = parents[i];
				}
			}
		} catch (NoSuchInterfaceException e1) {
			e1.printStackTrace();
		}

		String configFileName = System.getProperty("repository.config");

		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder db = factory.newDocumentBuilder();

			InputStream configIS = null;
			if (configFileName != null) {
				// look for the user-specified FracLite configuration file in
				// the file system
				configIS = new FileInputStream(new File(configFileName));

				if (configIS == null) {
					// look for the user-specified FracLite configuration file
					// in
					// the class path
					configIS = getClass().getResourceAsStream(configFileName);
				}
			}

			if (configIS == null) {
				// look for the default FracLite configuration file in the
				// class path
				configIS = getClass().getClassLoader().getResourceAsStream(
						FracLite.DEFAULT_MODULE_REPOSITORY_FILE_NAME);
			}

			Document doc = db.parse(configIS);

			NodeList modules = doc.getElementsByTagName("module");
			for (int i = 0; i < modules.getLength(); i++) {
				String moduleContent = null;

				Element moduleDescNode = (Element) modules.item(i);
				String moduleID = moduleDescNode.getAttribute("id");
				String moduleVersion = moduleDescNode.getAttribute("version");

				List<InterfaceType> interfaces = new ArrayList<InterfaceType>();

				interfaces.add(tf.createFcItfType(moduleID + "-"
						+ moduleVersion,
						"org.objectweb.fractal.deployment.local.api.IModule",
						false, false, false));

				NodeList moduleImports = moduleDescNode
						.getElementsByTagName("import");
				for (int j = 0; j < moduleImports.getLength(); j++) {
					Element importDescNode = (Element) moduleImports.item(j);
					String packageName = importDescNode.getAttribute("package");
					String packageVersion = importDescNode
							.getAttribute("version");

					InterfaceType itfType = tf
							.createFcItfType(
									packageName,
									"org.objectweb.fractal.deployment.local.api.IPackageExport",
									true, false, false);
					interfaces.add(itfType);

				}

				NodeList moduleExports = moduleDescNode
						.getElementsByTagName("export");
				for (int j = 0; j < moduleExports.getLength(); j++) {
					Element exportsDescNode = (Element) moduleExports.item(j);
					String packageName = exportsDescNode
							.getAttribute("package");
					String packageVersion = exportsDescNode
							.getAttribute("version");

					InterfaceType itfType = tf
							.createFcItfType(
									packageName,
									"org.objectweb.fractal.deployment.local.api.IPackageExport",
									false, false, false);
					interfaces.add(itfType);
				}

				interfaces
						.add(tf
								.createFcItfType(
										"physicalPackage",
										"org.objectweb.fractal.deployment.local.api.IPhysicalPackage",
										true, true, true));

				Type componentType = tf.createFcType(interfaces
						.toArray(new InterfaceType[] {}));

				Component moduleComponent = gf.newFcInstance(componentType,
						"module",
						"fr.jade.fraclite.deployment.GenericModuleComponent");

				Fractal.getNameController(moduleComponent).setFcName(
						moduleID + "-" + moduleVersion);

				Fractal.getContentController(bootstrapComposite)
						.addFcSubComponent(moduleComponent);

				NodeList nodes = moduleDescNode.getElementsByTagName("content");
				for (int j = 0; j < nodes.getLength(); j++) {
					Element moduleCont = (Element) moduleDescNode
							.getElementsByTagName("content").item(j);
					if (moduleCont != null) {
						String packageContent = moduleCont.getAttribute("file");
						if (packageContent != null) {
							IPhysicalPackage physPkg = (IPhysicalPackage) packageRepo
									.getPackage(packageContent).getFcInterface(
											"physicalPackage");
							Fractal.getBindingController(moduleComponent)
									.bindFc("physicalPackage", physPkg);
						}
					}
				}

				moduleBase.put(moduleID + "-" + moduleVersion, moduleComponent);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		// TODO: this should be externalized in a separate component
		new ModuleResolver().resolveModules();
		started = true;
	}

	public void stopFc() throws IllegalLifeCycleException {
		if (!started) {
			return;
		} else {
			started = false;
		}
	}

	public Component getModuleComponent(String ID) {
		return moduleBase.get(ID);
	}

	public void addModuleComponent(Component cmp) {
		try {
			String moduleName = Fractal.getNameController(cmp).getFcName();
			moduleBase.put(moduleName, cmp);
		} catch (NoSuchInterfaceException e) {
			e.printStackTrace();
		}
	}

	// TODO: this should be externalized in a separate component
	private class ModuleResolver {
		public void resolveModules() {
			Collection<Component> modules = moduleBase.values();

			// TODO: this is an extremely primitive implementation of the
			// resolving algorithm
			Iterator<Component> it1 = modules.iterator();
			while (it1.hasNext()) {
				Component currentModule = it1.next();
				try {
					System.out.println(Fractal.getNameController(currentModule)
							.getFcName());
				} catch (NoSuchInterfaceException e) {
					e.printStackTrace();
				}

				Object[] interfaces = currentModule.getFcInterfaces();
				for (int i = 0; i < interfaces.length; i++) {
					if (interfaces[i] instanceof Interface) {
						InterfaceType itfType = (InterfaceType) ((Interface) interfaces[i])
								.getFcItfType();
						boolean clientItf = itfType.isFcClientItf();
						String signature = itfType.getFcItfSignature();
						String name = itfType.getFcItfName();

						if (clientItf
								&& signature
										.equals("org.objectweb.fractal.deployment.local.api.IPackageExport")) {
							Iterator<Component> it2 = modules.iterator();
							while (it2.hasNext()) {
								Component potenatialExporter = it2.next();
								Object[] exporterItfs = potenatialExporter
										.getFcInterfaces();
								for (int j = 0; j < exporterItfs.length; j++) {
									if (exporterItfs[j] instanceof Interface) {
										InterfaceType exporterItfType = (InterfaceType) ((Interface) exporterItfs[j])
												.getFcItfType();
										if (!exporterItfType.isFcClientItf()
												&& exporterItfType
														.getFcItfSignature()
														.equals(signature)
												&& exporterItfType
														.getFcItfName().equals(
																name)) {
											try {
												Fractal
														.getBindingController(
																currentModule)
														.bindFc(
																name,
																potenatialExporter
																		.getFcInterface(name));
											} catch (NoSuchInterfaceException e) {
												e.printStackTrace();
											} catch (IllegalBindingException e) {
												e.printStackTrace();
											} catch (IllegalLifeCycleException e) {
												e.printStackTrace();
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
}
